多重界面
应该清楚,我们为Parser演化出来的名字空间定义并不是Parser提供给它的用户的界面。相反,它是为能方便地写出各个函数所需要的一组声明。Parser提供给它的用户的界面远比这简单得多:
namespace Parser {
double expr(bool);
}
幸运的是,Parser的这两个名字空间定义可以共存,这就使各种定义可以被用到最合适的地方。我们看到名字空间Parser被用于提供两种东西:
1)、实现分析器的所有函数的一个公共环境。
2)、分析器提供给它的用户的一个外部界面。
这样,驱动程序main()就应该只看到:
namespace Parser { // 给用户的界面
double expr(bool);
}
而实现Parser的那些函数则应该看到我们前面所确定的,对表述这些函数的共享环境而言最合适的界面,即
namespace Parser { // 给实现的界面
double prim(bool);
double term(bool);
double expr(bool);
using Lexer::get_token; // 使用Lexer的get_token
using Lexer::curr_tok; // 使用Lexer的curr_tok
using Error::error; // 使用Error的error
}
为实现所提供的界面比为用户提供的界面更大一些。如果这个界面真对的是现实程序里的一个现实规模的模块,那么它通常也会比用户能够看到的界面变化得更频繁些。将模块的用户(在这里是main()使用Parser)与这些变化隔离开是非常重要的。
我们并不需要两个互相独立的名字空间来描述这两个不同的界面。如果真需要的话我们也能做得到。设计界面是最基本的设计活动之一,而且是一种可以获得或者丧失重要利益的活动。因此,我们很值得去考虑到底想达到什么结果,并讨论若干不同的选择。
请记住,这里所介绍的解决方案是我们考虑过的各种解中最简单的,通常也是最好的。它的主要弱点是两个界面没有采用不同的名字,编译器不一定有足够的信息去检查两个名字空间定义的一致性。当然,即使编译器未必总有机会去检查这种一致性,它通常还是会这样做。进一步说,连接系统将能捕捉到编译器遗漏的大部分错误。
这里给出的解也是我将用于讨论物理模块化(9.3节)的解,也是我想推荐的没有进一步逻辑约束的解(8.2.7节)。
🔚